package com.moxiao.yfjh.module.blind.controller.app.controller.user;

import com.alipay.api.AlipayApiException;
import com.google.gson.JsonObject;
import com.moxiao.yfjh.framework.common.enums.UserTypeEnum;
import com.moxiao.yfjh.framework.common.pojo.CommonResult;
import com.moxiao.yfjh.framework.common.util.number.NumberUtils;
import com.moxiao.yfjh.framework.operatelog.core.annotations.OperateLog;
import com.moxiao.yfjh.module.blind.common.BlindMemberUserAuth;
import com.moxiao.yfjh.module.blind.common.InitFaceVerifyUtils;
import com.moxiao.yfjh.module.blind.controller.admin.member.vo.BlindMemberUserExportReqVO;
import com.moxiao.yfjh.module.blind.controller.admin.member.vo.BlindMemberUserUpdateReqVO;
import com.moxiao.yfjh.module.blind.controller.app.controller.user.vo.*;
import com.moxiao.yfjh.module.blind.controller.app.convert.BlindAuthConvert;
import com.moxiao.yfjh.module.blind.dal.dataobject.member.BlindMemberUserDO;
import com.moxiao.yfjh.module.blind.service.alipayLoginService.AlipayService;
import com.moxiao.yfjh.module.blind.service.member.BlindMemberUserService;
import com.moxiao.yfjh.module.system.api.oauth2.OAuth2TokenApi;
import com.moxiao.yfjh.module.system.api.oauth2.dto.OAuth2AccessTokenCreateReqDTO;
import com.moxiao.yfjh.module.system.api.oauth2.dto.OAuth2AccessTokenRespDTO;
import com.moxiao.yfjh.module.system.controller.admin.auth.vo.AuthLoginAstrologerRespVO;
import com.moxiao.yfjh.module.system.controller.admin.auth.vo.AuthLoginRespVO;
import com.moxiao.yfjh.module.system.controller.admin.user.vo.user.UserUpdatePasswordReqVO;
import com.moxiao.yfjh.module.system.enums.oauth2.OAuth2ClientConstants;
import com.moxiao.yfjh.module.system.mq.kafka.KafkaProducer;
import com.moxiao.yfjh.module.system.service.auth.AdminAuthService;
import com.moxiao.yfjh.module.system.service.user.AdminUserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.annotation.security.PermitAll;
import javax.validation.Valid;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import static com.moxiao.yfjh.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.moxiao.yfjh.framework.common.pojo.CommonResult.*;
import static com.moxiao.yfjh.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static com.moxiao.yfjh.module.blind.enums.ErrorCodeConstants.MEMBER_CODE_EXISTS;


@Tag(name = "用户 APP - 认证")
@RestController
@RequestMapping("/blind/member/login")
@Validated
@Slf4j
public class AppBlindAuthController {
    @Resource
    private BlindMemberUserService blindMemberUserService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private final static String smsSendKey = "SMS_CODE";
    @Resource
    private OAuth2TokenApi oauth2TokenApi;
    @Resource
    private AdminUserService userService;
    @Resource
    private InitFaceVerifyUtils initFaceVerifyUtils;
    @Resource
    private BlindMemberUserAuth authService;
    @Autowired
    private KafkaProducer kafkaProducer;
    @Resource
    private AdminAuthService adminAuthService;

    @PostMapping("/astrologer")
    @PermitAll
    @Operation(summary = "占星师登录：使用账号密码登录")
    @OperateLog(enable = false) // 避免 Post 请求被记录操作日志
    public CommonResult<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginAstrologerRespVO reqVO) {
        AuthLoginRespVO auth = adminAuthService.astrologerLogin(reqVO);
        auth.setType(3);
        return success(auth);
    }
    /**
     * 商家登录
     */
    @PostMapping("/app-merchant-login")
    @Operation(summary = "商家登录（用户名+密码登录）")
    @PermitAll
    @OperateLog(enable = false)
    public CommonResult<AuthLoginRespVO> merchantLogin(@RequestBody @Valid AppBlindAuthLoginPassVO reqVO) {
        int num = blindMemberUserService.getMerchantNum(reqVO.getUserName());
        if (num == 0) {
            return error(401, "未找到对应的商家信息");
        }
        AuthLoginRespVO auth = blindMemberUserService.merchantLogin(reqVO);
        auth.setType(2);
        return success(auth);
    }

    @PutMapping("/app-merchant-update-password")
    @Operation(summary = "商家用户-重置密码")
    public CommonResult<Boolean> updateUserPassword(@Valid @RequestBody UserUpdatePasswordReqVO reqVO) {
        userService.updateUserPassword(reqVO.getId(), reqVO.getPassword());
        return success(true);
    }


    /**
     * 用户注册
     */
    @PostMapping("/app-sms-register")
    @Operation(summary = "使用手机 + 验证码注册")
    public CommonResult<Boolean> smsRegister(@RequestBody @Valid AppBlindAuthLoginReqVO reqVO) {
        return success(blindMemberUserService.smsRegister(reqVO));
    }

    /**
     * 用户登录
     */
    @PostMapping("/app-sms-login")
    @Operation(summary = "使用手机 + 验证码登录")
    public CommonResult<AppBlindAuthLoginRespVO> smsLogin(@RequestBody @Valid AppBlindAuthLoginReqVO reqVO) {
        AppBlindAuthLoginRespVO auth = blindMemberUserService.smsLogin(reqVO);
        auth.setType(1);
        return success(auth);
    }

    /**
     * 用户登录
     */
    @PostMapping("/app-phone-login")
    @Operation(summary = "本地手机号一键登录")
    public CommonResult<AppBlindAuthLoginRespVO> phoneLogin(@RequestBody @Valid AppBlindAuthPhoneLoginReqVO reqVO) {
        AppBlindAuthLoginReqVO loginReqVO = new AppBlindAuthLoginReqVO();
        loginReqVO.setMobile(reqVO.getMobile());
        loginReqVO.setCode("123456");
        AppBlindAuthLoginRespVO auth = blindMemberUserService.phoneLogin(loginReqVO);
        auth.setType(1);
        return success(auth);
    }



    @PostMapping("/app-send-sms-code")
    @Operation(summary = "发送手机验证码")
    public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid AppBlindAuthSmsSendReqVO reqVO) {
        // 验证该手机号是否已经发送了验证码：验证码有效期5分钟
//        String redisKey = smsSendKey;
//        if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene())) {
//            redisKey += redisKey + "_" + SmsSceneEnum.MEMBER_UPDATE_MOBILE.getScene();
//        }
//        if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene())) {
//            redisKey += redisKey + "_" + SmsSceneEnum.MEMBER_RESET_PASSWORD.getScene();
//        }
//        if (Objects.equals(reqVO.getScene(), SmsSceneEnum.MEMBER_LOGIN.getScene())) {
//            redisKey += redisKey + "_" + SmsSceneEnum.MEMBER_LOGIN.getScene();
//        }
        //校验验证码
        String redisKey = smsSendKey + reqVO.getMobile();
        String code = stringRedisTemplate.opsForValue().get(redisKey);
        if (StringUtils.isNotBlank(code)) {
            throw exception(MEMBER_CODE_EXISTS);
        }
        blindMemberUserService.sendSmsCode(getLoginUserId(), reqVO);
        return success(true);
    }

    @PostMapping("/app-weChat-login")
    @Operation(summary = "App微信一键登录")
    public CommonResult<AppBlindAuthLoginRespVO> appWeChatLogin(@RequestBody @Valid AppWechatLoginReqVo reqVO) {
        return success(blindMemberUserService.wechatAppLogin(reqVO));
    }

    @PostMapping("/app-bind-phone")
    @Operation(summary = "App微信绑定手机号")
    public CommonResult<Boolean> appWechatBindPhone(@RequestBody @Valid AppWechatBindReqVo reqVO) {
        return success(true);
    }

    @GetMapping("/getDistance")
    @Operation(summary = "获取App两个人的距离")
    @Parameters({
            @Parameter(name = "latitude1", description = "精度", example = "110"),
            @Parameter(name = "longitude1", description = "纬度", example = "120"),
            @Parameter(name = "latitude2", description = "精度", example = "110"),
            @Parameter(name = "longitude2", description = "纬度", example = "120")
    })
    public CommonResult<Double> getDistance(
            @RequestParam(value = "latitude1", required = false) Double latitude1,
            @RequestParam(value = "longitude1", required = false) Double longitude1,
            @RequestParam(value = "latitude2", required = false) Double latitude2,
            @RequestParam(value = "longitude2", required = false) Double longitude2) {

        return success(NumberUtils.getDistance(latitude1, longitude1, latitude2, longitude2));

    }

    @PostMapping("/identityAuthentication")
    @Operation(summary = "用户身份信息认证")
    public CommonResult<String> identityAuthentication(@RequestBody @Valid InitFaceVerifyReqVo initFaceVerifyReqVo) {
        initFaceVerifyReqVo.setUserId(getLoginUserId());
        // 设置缓存key
        String redis_key = "blind_cert_" + initFaceVerifyReqVo.getCertNo();
        String value = stringRedisTemplate.opsForValue().get(redis_key);
        if (StringUtils.isNotBlank(value)) {
            return error(500, "您每24小时只能认证一次，请稍后再试！");
        }
        initFaceVerifyReqVo.setUserId(getLoginUserId());
        Map<String, String> map = initFaceVerifyUtils.initFaceVerify(initFaceVerifyReqVo);
        if (MapUtils.isNotEmpty(map)) {
            // 设置缓存，缓存时间24小时
            stringRedisTemplate.opsForValue().set(redis_key, initFaceVerifyReqVo.getCertNo(), 24, TimeUnit.HOURS);
            String certifyId = map.get("certifyId");
            if (StringUtils.isEmpty(certifyId)) {
                return error(500, map.get("message"));
            }
            // 认证成功，修改认证状态，修改用户的性别，年龄，生日，星座
            MemberUserAuthVO authVO = initFaceVerifyUtils.getIdCard(initFaceVerifyReqVo.getCertNo());
            authVO.setId(getLoginUserId());
            blindMemberUserService.updateIdNumberAuth(authVO);
            return success(certifyId);
        }
        return success(null);
    }

    /**
     * 用户人像实名认证（公安比对）
     * 一日一次
     * @return
     */

    @PostMapping("/selfieIdNumberAuth")
    @Operation(summary = "用户人脸实名认证，1001 : 身份证号与姓名匹配，人脸判断为同一人，1002 : 身份证号与姓名匹配，人脸不能确定为同一人，1003：身份证号与姓名匹配，人脸判断为不同人")
    public CommonResult<Boolean> selfieIdNumberAuth() throws ExecutionException, InterruptedException {
        BlindMemberUserDO memberUser = blindMemberUserService.getMemberUser(getLoginUserId());
        if (Objects.isNull(memberUser)){
            return error(401, "获取个人信息失败");
        }
        String redis_key = "blind_avatar_auth_" + memberUser.getId();
        String value = stringRedisTemplate.opsForValue().get(redis_key);
        if (StringUtils.isNotBlank(value)) {
            return error(500, "您每24小时只能认证一次，请稍后再试！");
        }
        //  kafka 生产消息发送
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("userId", memberUser.getId());
        jsonObject.addProperty("Avatar", memberUser.getAvatar());
        jsonObject.addProperty("IdNumber", memberUser.getIdNumber());
        jsonObject.addProperty("Name", memberUser.getName());
        kafkaProducer.addSendAuthAvatar(jsonObject);
        // 设置缓存 用于时间校验
        stringRedisTemplate.opsForValue().set(redis_key, memberUser.getUserNo(), 24, TimeUnit.HOURS);
        return buildSuccess(Boolean.TRUE, "");
    }


    @PutMapping("/update-mobile")
    @Operation(summary = "修改用户手机")
    public CommonResult<Boolean> updateUserMobile(@RequestBody @Valid AppBlindUserUpdateMobileReqVO reqVO) {
        Long loginUserId = getLoginUserId();
        if (Objects.isNull(loginUserId)){
            return error(401, "获取个人信息失败");
        }
        blindMemberUserService.updateUserMobile(loginUserId, reqVO);
        return success(true);
    }

    /**
     * 随机页缓存key
     */
    private final static String BASIC_MATCH_BLIND_MEMBER = "basic_match_blind_member:";
    @PutMapping("/update-pesence")
    @Operation(summary = "修改在线状态")
    public CommonResult<Boolean> updateUserPesence(@RequestBody @Valid BlindMemberUserUpdateReqVO updateReqVO) {
        Long loginUserId = getLoginUserId();
        if (Objects.isNull(loginUserId)){
            return error(401, "获取个人信息失败");
        }
        updateReqVO.setId(loginUserId);
        blindMemberUserService.updateMemberUser(updateReqVO);
        if (Objects.equals(updateReqVO.getPesence(),(byte) 0)){

            Set<String> scan = scan(BASIC_MATCH_BLIND_MEMBER + loginUserId,10000);
            stringRedisTemplate.delete(scan);
        }
        return success(true);
    }

    public Set<String> scan (String pattern, int count) {
        Set<String> keys = new HashSet<>();
        RedisSerializer serializer = stringRedisTemplate.getKeySerializer();
        ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(count).build();
        Cursor<byte[]> cursor = stringRedisTemplate.execute(connection -> connection.scan(scanOptions), true);
        while (cursor.hasNext()) {
            keys.add(String.valueOf(serializer.deserialize(cursor.next())));
        }
        return keys;
    }




    @PostMapping("/app-alipay-login")
    @Operation(summary = "支付宝登录")
    public CommonResult<AppBlindAuthLoginRespVO> alipayLogin(@RequestBody @Valid String authToken) throws AlipayApiException {
        String mobileByCode = AlipayService.getMobileByCode(authToken);
        List<BlindMemberUserDO> memberUserList = blindMemberUserService.getMemberUserList(new BlindMemberUserExportReqVO().setMobile(mobileByCode));
        if (Objects.nonNull(memberUserList) && mobileByCode != null) {
            OAuth2AccessTokenRespDTO accessTokenRespDTO = oauth2TokenApi.createAccessToken(new OAuth2AccessTokenCreateReqDTO()
                    .setUserId(memberUserList.get(0).getId()).setUserType(UserTypeEnum.MEMBER.getValue())
                    .setClientId(OAuth2ClientConstants.CLIENT_ID_DEFAULT));
            return success(BlindAuthConvert.INSTANCE.convert(accessTokenRespDTO));
        }
        return CommonResult.error(555, "请先绑定手机号");
    }
}
